package de.unikoblenz.west.csxpoi.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.query.BindingSet;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.http.HTTPRepository;

/**
 * REST interface for POIs.
 */
public class RestPois extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private RestRequest restRequest;

	@Override
	public void init() throws ServletException {
		restRequest = new RestRequest(this.getServletContext());
	}

	/**
	 * Handles GET requests.
	 */
	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		if (ServletHelper.isCollectionRequest(request)) {

			try {
				try {
					String[] args = new String[6];
					args[0] = ServletHelper.resolveStringParameter(request,
							"user");
					args[1] = ServletHelper.resolveStringParameter(request,
							"query");
					args[2] = ServletHelper.resolveDoubleParameter(request,
							"left") + "";
					args[3] = ServletHelper.resolveDoubleParameter(request,
							"right") + "";
					args[4] = ServletHelper.resolveDoubleParameter(request,
							"top") + "";
					args[5] = ServletHelper.resolveDoubleParameter(request,
							"bottom") + "";

					List<PoiWrapper> pois = restRequest.getPois(args);

					XmlWriter.writePoisResponse(response, pois);

				} catch (Exception e) {
					throw new ServerException();
				}

			} catch (ServerException e) {
				XmlWriter.writePoisResponse(response, null);
			}
		}
		
		
		//TODO: Process isMemberRequest-Requests via RestRequest-Object
		else { // ServletHelper.isMemberRequest(request) == true

			try {
				String id = ServletHelper.resolveStringParameter(request, "id");

				PoiWrapper poi = null;

				Repository repository = new HTTPRepository(
						Constants.SESAME_SERVER, Constants.REPOSITORY_ID);

				try {
					repository.initialize();
					RepositoryConnection connection = repository
							.getConnection();

					try {
						poi = PoiWrapper.createFromUri(id, connection);
					} finally {
						connection.close();
					}

					XmlWriter.writePoiResponse(response, poi);

				} catch (RepositoryException e) {
					throw new ServerException();
				} catch (MalformedQueryException e) {
					throw new ServerException();
				} catch (QueryEvaluationException e) {
					throw new ServerException();
				}

			} catch (ServerException e) {
				XmlWriter.writePoiResponse(response, null);
			}

		}
	}

	//TODO change POST, PUT, DELETE requests to Request-objects
	/**
	 * Handles POST requests.
	 */
	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		if (ServletHelper.isCollectionRequest(request)) {

			try {
				String name = ServletHelper.resolveStringParameter(request,
						"name");
				String user = ServletHelper.resolveStringParameter(request,
						"user");
				double latitude = ServletHelper.resolveDoubleParameter(request,
						"lat");
				double longitude = ServletHelper.resolveDoubleParameter(
						request, "long");
				double altitude = ServletHelper.resolveDoubleParameter(request,
						"alt");

				// TODO get event parameters
				String startDate = ServletHelper.resolveStringParameter(
						request, "start");
				String endDate = ServletHelper.resolveStringParameter(request,
						"end");

				List<String> categories = ServletHelper
						.resolveStringListParameter(request, "category");
				List<String> equivalents = ServletHelper
						.resolveStringListParameter(request, "equivalent");

				Repository repository = new HTTPRepository(
						Constants.SESAME_SERVER, Constants.REPOSITORY_ID);

				try {
					repository.initialize();
					RepositoryConnection connection = repository
							.getConnection();

					PoiWrapper poi = new PoiWrapper();

					try {
						String uri = OntologyHelper.generatePoiUri(connection);
						poi.setUri(uri);
						poi.setName(name);
						poi.setLatitude(latitude);
						poi.setLongitude(longitude);
						poi.setAltitude(altitude);

						// TODO set event attributes
						poi.setStartDate(startDate);
						poi.setEndDate(endDate);

						for (String categoryUri : categories) {
							poi.addCategory(categoryUri, connection);
						}
						for (String equivalentUri : equivalents) {
							poi.addEquivalent(equivalentUri, connection);
						}

						ValueFactory valueFactory = connection
								.getValueFactory();

						String contributionId = OntologyHelper
								.generateContributionUri(connection);

						if (contributionId == null)
							throw new ServerException();

						URI uriUri = valueFactory.createURI(poi.getUri());
						URI contributionUri = valueFactory
								.createURI(contributionId);
						URI rdfTypeUri = valueFactory
								.createURI(Constants.URI_RDF_TYPE);
						URI poiCreationUri = valueFactory
								.createURI(Constants.URI_BASE_POI_CREATION);
						URI affectsUri = valueFactory
								.createURI(Constants.URI_BASE_AFFECTS_POI);
						URI issuedAtUri = valueFactory
								.createURI(Constants.URI_BASE_ISSUED_AT);
						URI dateTimeUri = valueFactory
								.createURI(Constants.URI_XSD_DATE_TIME);
						Literal dateTimeLiteral = valueFactory.createLiteral(
								OntologyHelper.generateDateTimeString(),
								dateTimeUri);
						URI userUri = valueFactory.createURI(user);
						URI issuedByUri = valueFactory
								.createURI(Constants.URI_BASE_ISSUED_BY);
						URI stringUri = valueFactory
								.createURI(Constants.URI_XSD_STRING);
						URI labelUri = valueFactory
								.createURI(Constants.URI_RDFS_LABEL);
						Literal nameLiteral = valueFactory.createLiteral(
								poi.getName(), stringUri);
						URI decimalUri = valueFactory
								.createURI(Constants.URI_XSD_DECIMAL);
						URI latUri = valueFactory
								.createURI(Constants.URI_GEO_LAT);
						Literal latLiteral = valueFactory.createLiteral(""
								+ poi.getLatitude(), decimalUri);
						URI longUri = valueFactory
								.createURI(Constants.URI_GEO_LONG);
						Literal longLiteral = valueFactory.createLiteral(""
								+ poi.getLongitude(), decimalUri);
						URI altUri = valueFactory
								.createURI(Constants.URI_GEO_ALT);
						Literal altLiteral = valueFactory.createLiteral(""
								+ poi.getAltitude(), decimalUri);
						URI poiUri = valueFactory
								.createURI(Constants.URI_BASE_POI);

						// TODO event URIs
						URI startUri = valueFactory
								.createURI(Constants.URI_HAS_START_DATETIME);
						Literal startLiteral = valueFactory.createLiteral(
								poi.getStartDate(), stringUri);
						URI endUri = valueFactory
								.createURI(Constants.URI_HAS_END_DATETIME);
						Literal endLiteral = valueFactory.createLiteral(
								poi.getEndDate(), stringUri);

						connection.add(contributionUri, rdfTypeUri,
								poiCreationUri);
						connection.add(contributionUri, affectsUri, uriUri);
						connection.add(contributionUri, issuedAtUri,
								dateTimeLiteral);
						connection.add(contributionUri, issuedByUri, userUri);
						connection.add(uriUri, labelUri, nameLiteral);
						connection.add(uriUri, latUri, latLiteral);
						connection.add(uriUri, longUri, longLiteral);
						connection.add(uriUri, altUri, altLiteral);

						// FIXME
						// add event triples
						if (!poi.getStartDate().equals(""))
							connection.add(uriUri, startUri, startLiteral);
						if (!poi.getEndDate().equals(""))
							connection.add(uriUri, endUri, endLiteral);

						if (poi.getCategories().isEmpty()) {
							connection.add(uriUri, rdfTypeUri, poiUri);
						} else {
							for (CategoryWrapper category : poi.getCategories()) {
								URI categoryUri = valueFactory
										.createURI(category.getUri());
								connection.add(uriUri, rdfTypeUri, categoryUri);
							}
						}
					} finally {
						connection.close();
					}

					XmlWriter.writePoiResponse(response, poi);

				} catch (RepositoryException e) {
					throw new ServerException();
				} catch (MalformedQueryException e) {
					throw new ServerException();
				} catch (QueryEvaluationException e) {
					throw new ServerException();
				}
			} catch (ServerException e) {
				XmlWriter.writePoiResponse(response, null);
			}
		} else { // ServletHelper.isMemberRequest(request)
			// unused
		}
	}

	/**
	 * Handles PUT requests.
	 */
	@Override
	protected void doPut(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		if (ServletHelper.isMemberRequest(request)) {

			try {
				String id = ServletHelper.resolveStringParameter(request, "id");

				PoiWrapper poi = null;

				if (request.getParameter("changes") != null
						|| request.getParameter("adds") != null
						|| request.getParameter("deletes") != null) {

					Repository repository = new HTTPRepository(
							Constants.SESAME_SERVER, Constants.REPOSITORY_ID);

					try {
						repository.initialize();
						RepositoryConnection connection = repository
								.getConnection();

						try {

							ValueFactory valueFactory = connection
									.getValueFactory();

							String user = ServletHelper.resolveStringParameter(
									request, "user");
							String value = ServletHelper
									.resolveStringParameter(request, "value");

							URI poiUri = valueFactory.createURI(id);
							URI userUri = valueFactory.createURI(user);
							String contributionId = OntologyHelper
									.generateContributionUri(connection);
							URI contributionUri = valueFactory
									.createURI(contributionId);
							URI dateTimeUri = valueFactory
									.createURI(Constants.URI_XSD_DATE_TIME);
							Literal dateTimeLiteral = valueFactory
									.createLiteral(OntologyHelper
											.generateDateTimeString(),
											dateTimeUri);
							URI rdfTypeUri = valueFactory
									.createURI(Constants.URI_RDF_TYPE);
							URI poiModificationUri = valueFactory
									.createURI(Constants.URI_BASE_POI_MODIFICATION);
							URI affectsPoiUri = valueFactory
									.createURI(Constants.URI_BASE_AFFECTS_POI);
							URI issuedAtUri = valueFactory
									.createURI(Constants.URI_BASE_ISSUED_AT);
							URI issuedByUri = valueFactory
									.createURI(Constants.URI_BASE_ISSUED_BY);
							URI poiPropertyValueUri = valueFactory
									.createURI(Constants.URI_BASE_POI_PROPERTY_VALUE);

							connection.add(contributionUri, rdfTypeUri,
									poiModificationUri);
							connection.add(contributionUri, affectsPoiUri,
									poiUri);
							connection.add(contributionUri, issuedAtUri,
									dateTimeLiteral);
							connection.add(contributionUri, issuedByUri,
									userUri);

							if (request.getParameter("changes") != null) {
								String changedProperty = ServletHelper
										.resolveStringParameter(request,
												"changes");
								URI changedPropertyUri = valueFactory
										.createURI(changedProperty);
								URI changesPoiPropertyUri = valueFactory
										.createURI(Constants.URI_BASE_CHANGES_POI_PROPERTY);
								URI stringUri = valueFactory
										.createURI(Constants.URI_XSD_STRING);
								Literal valueLiteral = valueFactory
										.createLiteral(value, stringUri);
								connection.add(contributionUri,
										changesPoiPropertyUri,
										changedPropertyUri);
								connection.add(contributionUri,
										poiPropertyValueUri, valueLiteral);
							} else if (request.getParameter("adds") != null) {
								String addedProperty = ServletHelper
										.resolveStringParameter(request, "adds");
								URI addedPropertyUri = valueFactory
										.createURI(addedProperty);
								URI addsPoiPropertyUri = valueFactory
										.createURI(Constants.URI_BASE_ADDS_POI_PROPERTY);
								connection.add(contributionUri,
										addsPoiPropertyUri, addedPropertyUri);
								URI valueUri = valueFactory.createURI(value);
								connection.add(contributionUri,
										poiPropertyValueUri, valueUri);
							} else if (request.getParameter("deletes") != null) {
								String deletedProperty = ServletHelper
										.resolveStringParameter(request,
												"deletes");
								URI deletedPropertyUri = valueFactory
										.createURI(deletedProperty);
								URI deletesPoiPropertyUri = valueFactory
										.createURI(Constants.URI_BASE_DELETES_POI_PROPERTY);
								connection.add(contributionUri,
										deletesPoiPropertyUri,
										deletedPropertyUri);
								URI valueUri = valueFactory.createURI(value);
								connection.add(contributionUri,
										poiPropertyValueUri, valueUri);
							}

							poi = PoiWrapper.createFromUri(id, connection);

						} finally {
							connection.close();
						}

						XmlWriter.writePoiResponse(response, poi);

					} catch (RepositoryException e) {
						throw new ServerException();
					} catch (MalformedQueryException e) {
						throw new ServerException();
					} catch (QueryEvaluationException e) {
						throw new ServerException();
					}
				}
			} catch (ServerException e) {
				XmlWriter.writePoiResponse(response, null);
			}

		} else { // ServletHelper.isCollectionRequest(request)
			// unused
		}
	}

	/**
	 * Handles DELETE requests.
	 */
	@Override
	protected void doDelete(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		if (ServletHelper.isMemberRequest(request)) {

			try {
				String id = ServletHelper.resolveStringParameter(request, "id");

				PoiWrapper poi = null;

				Repository repository = new HTTPRepository(
						Constants.SESAME_SERVER, Constants.REPOSITORY_ID);

				try {
					repository.initialize();
					RepositoryConnection connection = repository
							.getConnection();

					try {
						poi = PoiWrapper.createFromUri(id, connection);

						ValueFactory valueFactory = connection
								.getValueFactory();

						String user = ServletHelper.resolveStringParameter(
								request, "user");

						URI poiUri = valueFactory.createURI(id);
						URI userUri = valueFactory.createURI(user);
						String contributionId = OntologyHelper
								.generateContributionUri(connection);
						URI contributionUri = valueFactory
								.createURI(contributionId);
						URI rdfTypeUri = valueFactory
								.createURI(Constants.URI_RDF_TYPE);
						URI poiRemovalUri = valueFactory
								.createURI(Constants.URI_BASE_POI_REMOVAL);
						URI affectsPoiUri = valueFactory
								.createURI(Constants.URI_BASE_AFFECTS_POI);
						URI issuedAtUri = valueFactory
								.createURI(Constants.URI_BASE_ISSUED_AT);
						URI dateTimeUri = valueFactory
								.createURI(Constants.URI_XSD_DATE_TIME);
						Literal dateTimeLiteral = valueFactory.createLiteral(
								OntologyHelper.generateDateTimeString(),
								dateTimeUri);
						URI issuedByUri = valueFactory
								.createURI(Constants.URI_BASE_ISSUED_BY);

						connection.add(contributionUri, rdfTypeUri,
								poiRemovalUri);
						connection.add(contributionUri, affectsPoiUri, poiUri);
						connection.add(contributionUri, issuedAtUri,
								dateTimeLiteral);
						connection.add(contributionUri, issuedByUri, userUri);

					} finally {
						connection.close();
					}

					XmlWriter.writePoiResponse(response, poi);

				} catch (RepositoryException e) {
					throw new ServerException();
				} catch (MalformedQueryException e) {
					throw new ServerException();
				} catch (QueryEvaluationException e) {
					throw new ServerException();
				}

			} catch (ServerException e) {
				XmlWriter.writePoiResponse(response, null);
			}
		} else { // ServletHelper.isCollectionRequest(request)
			// unused
		}
	}
}
